package arkham.gui;

import javax.swing.*;

import arkham.data.*;
import arkham.data.Board.Location;
import arkham.data.Board.OtherWorld;
import arkham.net.ClientBase;
import arkham.net.ServerBase;
import arkham.net.messages.CommandMessage;
import arkham.net.messages.CommandMessage.Command;
import arkham.net.messages.FileMessage;
import arkham.net.messages.InfoMessage;
import arkham.net.messages.Message;
import arkham.net.messages.MessageEndpoint;
import arkham.util.AHLogger;
import arkham.util.ImageUtils;
import arkham.util.Pair;

import java.util.*;
import java.util.List;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.*;

public class LobbyScreen extends JFrame implements MessageEndpoint, WindowListener
{

	private static final long serialVersionUID = 8086952997994368838L;
	private int width, height;
	private boolean host, newGame;
	private List<String> expansions;
	private List<String> omittedCards;
	private HashSet<String> readyPlayers;
	private ClientBase client;
	private ServerBase server;
	private String username, hostusername;
	private String loadString;
	private List<String> players;
	private String[] investigators;
	private String[] owners;
	private JLabel[] investigatorLabels;
	private JLabel[] ownerLabels;
	private GameLoader loader;
	private JButton startGame;
	private int numFilesToLoad;
	
	/* Gui components */
	private JPanel expansionsPanel;
	private JPanel playerListPanel;
	private JList playerListDisplay;
	private JPanel mainPanel;
	private JPanel chatBoxPanel;
	private ChatManager chatManager;
	
	private Font arkhamFont;
	private BufferedImage arrowsGrid;
	private ImageIcon leftArrow, rightArrow, upArrow, downArrow;
	
	private LobbyScreen(String loadString)
	{
		width = 800;
		height = 570;
		players = new ArrayList<String>();
		readyPlayers = new HashSet<String>();
		investigators = new String[8];
		investigatorLabels = new JLabel[8];
		owners = new String[8];
		ownerLabels = new JLabel[8];
		arkhamFont = ImageUtils.loadFont("fonts/myoldrem.ttf").deriveFont(Font.PLAIN, 24);
		this.loadString = loadString;
		this.setTitle("Arkham Horror");
		this.setSize(new Dimension(width, height));
		this.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
		this.setIconImage(ImageUtils.loadImage("img/ahicon.png"));
		this.addWindowListener(this);

		/* Set the initial position to middle of screen */
		Toolkit toolkit = Toolkit.getDefaultToolkit();
		Dimension screensize = toolkit.getScreenSize();
		this.setLocation((screensize.width - width) / 2, (screensize.height - height - 30) / 2);
		
		//TODO: This will be set to true in the case of a loaded game
		newGame = true;
	}
	
	/**
	 * Starts the lobby as a client
	 * @param client the underlying network transmission client object
	 */
	public LobbyScreen(ClientBase client, String loadString)
	{
		this(loadString);
		host = false;
		this.client = client;
		username = client.getUsername();
		hostusername = "";
		parseLoadString(loadString);
		client.setMessageEndpoint(this);
		players.add(username);
		initUI();
		initComms();
	}
	
	/**
	 * Starts the lobby as the host
	 * @param server the underlying network transmission server object
	 */
	public LobbyScreen(ServerBase server, String loadString)
	{
		this(loadString);
		host = true;
		parseLoadString(loadString);
		hostusername = username = server.getUsername();
		this.server = server;
		server.setMessageEndpoint(this);
		players.add(username);
		initUI();
		initComms();
	}
	
	public LobbyScreen(String username, int test)
	{
		this("");
		this.username = username;
		host = true;
		hostusername = username;
		players.add(username);
		players.add("abcdefghij");
		players.add("jerry");
		initUI();
	}
	
	/* ------------------- Initialization methods -------------- */
	
	private void initUI()
	{
		loadArrows();
		this.setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
		initExpansionsPanel();
		initMainPanel();
		initPlayersPanel();
		initChatBox();
		JPanel topPanel = new JPanel();
		topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.X_AXIS));
		topPanel.add(expansionsPanel);
		topPanel.add(mainPanel);
		topPanel.add(playerListPanel);
		this.add(topPanel);
		this.add(chatBoxPanel);
		updatePlayerDisplay();
		this.setVisible(true);
		Insets in = this.getInsets();
		this.setSize(new Dimension(width + in.left + in.right, height + in.top + in.bottom));
	}
	
	private void initComms()
	{
		if (host)
		{
			// for servers
			// link chat box with an action listener
			chatManager.getChatField().addActionListener(new ActionListener()
			{
				@Override
				public void actionPerformed(ActionEvent e) {
					if (chatManager.getChatFieldText().length() > 0)
					{
						if (chatManager.getChatFieldText().startsWith("/"))
						{
							chatManager.slashCommand(chatManager.clearChatFieldText().substring(1));
							return;
						}
						String chat = username + ": " + chatManager.clearChatFieldText();
						chatManager.displayMessage(chat);
						server.serverTextMessage(chat);
					}
				}
			});
			
		}
		else
		{
			// for clients
			// link chat box with an action listener
			chatManager.getChatField().addActionListener(new ActionListener()
			{
				@Override
				public void actionPerformed(ActionEvent e) {
					if (chatManager.getChatFieldText().length() > 0)
					{
						if (chatManager.getChatFieldText().startsWith("/"))
						{
							chatManager.slashCommand(chatManager.clearChatFieldText().substring(1));
							return;
						}
						String text = chatManager.clearChatFieldText();
						String chat = username + ": " + text;
						chatManager.displayMessage(chat);
						client.sendTextMessage(text);
					}
				}
			});
					
			// request for info from server
			client.sendInfoMessage("hostUsername");
			client.sendInfoMessage("playerList");
			client.sendInfoMessage("ownerList");
			client.sendInfoMessage("md5Info");
		}
	}
	
	/**
	 * This will load the arrow images
	 */
	private void loadArrows()
	{
		arrowsGrid = ImageUtils.loadImage("img/arrowsgrid.png");
		leftArrow = new ImageIcon(ImageUtils.resizeImage(
				arrowsGrid.getSubimage(0, 152, 57, 57), 30, 30)); // y=78,152,228
		rightArrow = new ImageIcon(ImageUtils.resizeImage(
				arrowsGrid.getSubimage(80, 78, 57, 57), 30, 30));
		upArrow = new ImageIcon(ImageUtils.resizeImage(
				arrowsGrid.getSubimage(161, 0, 57, 57), 30, 30));
		downArrow = new ImageIcon(ImageUtils.resizeImage(
				arrowsGrid.getSubimage(242, 0, 57, 57), 30, 30));
	}
	
	/**
	 * This method parses the loading string to figure out which
	 * expansions are added or not added.
	 * @param loadString the contents of the loading file 
	 */
	private void parseLoadString(String loadString)
	{
		// TODO: We will assume only the base set is loaded for now.
		expansions = new ArrayList<String>();
		omittedCards = new ArrayList<String>();
		loader = new GameLoader();
		if (host)
		{
			loader.loadMasterDataFromFile("xml/master.txt");
			boolean result = loader.loadAsServer(loadString);
			if (!result)
			{
				shutdown();
			}
		}
		else
		{
			loader.loadMasterDataFromText(client.getMasterData());
			boolean result = loader.loadAsClient(loadString);
			if (!result)
			{
				shutdown();
			}
		}
		expansions.addAll(loader.getExpansionsInPlay());
		
	}
	
	private void initExpansionsPanel()
	{
		JTextArea expList = new JTextArea(22, 25);
		expList.setEditable(false);
		expList.setCursor(null);
		expList.setOpaque(false);
		expList.setFocusable(false);
		
		StringBuffer sb = new StringBuffer();
		for (String s : expansions)
		{
			sb.append(s);
			sb.append("\n");
		}
		
		if (!omittedCards.isEmpty())
		{
			sb.append("\nOmitted:\n");
		}
		for (String s : omittedCards)
		{
			sb.append(s);
			sb.append("\n");
		}
		if (sb.length() > 1) 
		{
			sb.deleteCharAt(sb.length() - 1);
		}
		expList.setText(sb.toString());
		expList.setWrapStyleWord(false);
		expList.setLineWrap(true);
		expList.setVisible(true);
		JScrollPane scrollPanel = new JScrollPane(expList);
		scrollPanel.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		scrollPanel.setBorder(BorderFactory.createCompoundBorder(
				BorderFactory.createLineBorder(Color.black),
				BorderFactory.createEmptyBorder(2,2,2,2)));
		ImageUtils.setFixedSize(scrollPanel, 130, 370);
		scrollPanel.setAlignmentX(Component.CENTER_ALIGNMENT);
		expansionsPanel = new JPanel();
		expansionsPanel.setLayout(new BoxLayout(expansionsPanel, BoxLayout.Y_AXIS));
		JLabel expTitle = new JLabel("Scenario Info", JLabel.CENTER);
		expTitle.setFont(arkhamFont);
		expTitle.setAlignmentX(Component.CENTER_ALIGNMENT);
		expansionsPanel.add(expTitle);
		expansionsPanel.add(scrollPanel);
		expansionsPanel.setAlignmentY(Component.TOP_ALIGNMENT);
	}
	
	@SuppressWarnings("serial")
	private void initPlayersPanel()
	{
		playerListPanel = new JPanel();
		playerListPanel.setPreferredSize(new Dimension(120, 400));
		playerListPanel.setBackground(new Color(192, 192, 192));
		playerListDisplay = new JList();
		playerListDisplay.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		ImageUtils.setFixedSize(playerListDisplay, 108, 370);
		playerListDisplay.setBorder(BorderFactory.createEmptyBorder(10, 3, 10, 3));
		playerListDisplay.setAlignmentX(Component.CENTER_ALIGNMENT);
		// Create a cell renderer to display the host's name in blue
		playerListDisplay.setCellRenderer(new DefaultListCellRenderer()
			{
				public Component getListCellRendererComponent(JList list,
															  Object value,
															  int index,
															  boolean isSelected,
															  boolean cellHasFocus)
				{
					super.getListCellRendererComponent(list,
							  						   value,
							  						   index,
							  						   isSelected,
							  						   cellHasFocus);
					if (value.equals(hostusername))
					{
						this.setForeground(Color.blue);
					}
					return this;
				}
			});
		
		playerListPanel.setLayout(new BoxLayout(playerListPanel, BoxLayout.Y_AXIS));
		JLabel pltitle = new JLabel("Players", JLabel.CENTER);
		pltitle.setFont(arkhamFont);
		pltitle.setAlignmentX(Component.CENTER_ALIGNMENT);
		playerListPanel.add(pltitle);
		playerListPanel.add(playerListDisplay);
		
		playerListPanel.setBorder(BorderFactory.createRaisedBevelBorder());
		playerListPanel.setAlignmentY(Component.TOP_ALIGNMENT);
	}
	
	/**
	 * Initializes the main panel in the middle of the screen
	 */
	private void initMainPanel()
	{
		// initialize the jlabels for the investigators and owners
		for (int i = 0; i < 8; i++)
		{
			investigators[i] = "< None >";
			investigatorLabels[i] = new JLabel("< None >");
			investigatorLabels[i].setHorizontalAlignment(JLabel.LEFT);
			investigatorLabels[i].setFont(new Font("Arial", Font.BOLD, 14));
			ImageUtils.setFixedSize(investigatorLabels[i], 150, 30);
			owners[i] = null;
			ownerLabels[i] = new JLabel("");
			ownerLabels[i].setHorizontalAlignment(JLabel.LEFT);
			ownerLabels[i].setFont(new Font("Arial", Font.BOLD, 14));
			ImageUtils.setFixedSize(ownerLabels[i], 150, 30);
		}
		
		// use a grid bag layout
		mainPanel = new JPanel();
		mainPanel.setLayout(new GridBagLayout());
		GridBagConstraints gc = new GridBagConstraints();
		// title
		ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.5, 0, 1, 0);
		gc.gridwidth = 1;
		JLabel mptitle = new UnderlinedLabel("Player");
		mptitle.setFont(arkhamFont);
		mainPanel.add(mptitle, gc);
		ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.5, 0, 2, 0);
		gc.gridwidth = 1;
		JLabel invtitle = new UnderlinedLabel("Investigator");
		invtitle.setFont(arkhamFont);
		mainPanel.add(invtitle, gc);
		
		for (int i = 0; i < 8; i++)
		{
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.1, 0, 0, i + 1);
			JLabel numberTag = new JLabel(String.valueOf(i+1) + ". ");
			numberTag.setAlignmentX(Component.LEFT_ALIGNMENT);
			numberTag.setFont(new Font("Arial", Font.BOLD, 14));
			ImageUtils.setFixedSize(numberTag, 50, 40);
			mainPanel.add(numberTag, gc);
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.2, 0, 1, i + 1);
			mainPanel.add(ownerLabels[i], gc);
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.5, 0, 2, i + 1);
			mainPanel.add(investigatorLabels[i], gc);
			// configure buttons
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.05, 0, 3, i + 1);
			JButton left = new RoundButton(leftArrow);
			left.setActionCommand("Left " + i);
			left.addActionListener(mainButtonListener);
			mainPanel.add(left, gc);
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.05, 0, 4, i + 1);
			JButton up = new RoundButton(upArrow);
			up.setActionCommand("Up " + i);
			up.addActionListener(mainButtonListener);
			mainPanel.add(up, gc);
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.05, 0, 5, i + 1);
			JButton down = new RoundButton(downArrow);
			down.setActionCommand("Down " + i);
			down.addActionListener(mainButtonListener);
			mainPanel.add(down, gc);
			ImageUtils.initGBC(gc, GridBagConstraints.HORIZONTAL, 0.05, 0, 6, i + 1);
			JButton right = new RoundButton(rightArrow);
			right.setActionCommand("Right " + i);
			right.addActionListener(mainButtonListener);
			mainPanel.add(right, gc);
			if (!host)
			{
				// only hosts get to manipulate the players
				left.setVisible(false);
				right.setVisible(false);
				up.setVisible(false);
				down.setVisible(false);
			}
		}
		
		ImageUtils.initGBC(gc, GridBagConstraints.BOTH, 0, 0, 0, 9);
		gc.gridwidth = 0;
		JPanel sbPanel = new JPanel();
		sbPanel.setLayout(new BoxLayout(sbPanel, BoxLayout.X_AXIS));
		startGame = new JButton();
		if (host)
		{
			startGame.setText("Start Game");
		}
		else
		{
			startGame.setText("Loading...");
			startGame.setEnabled(false);
		}
		startGame.addActionListener(startGameListener);
		ImageUtils.setFixedSize(startGame, 100, 40);
		startGame.setAlignmentX(CENTER_ALIGNMENT);
		sbPanel.add(Box.createHorizontalGlue());
		sbPanel.add(Box.createRigidArea(new Dimension(100, 0)));
		sbPanel.add(startGame);
		sbPanel.add(Box.createRigidArea(new Dimension(100, 0)));
		sbPanel.add(Box.createHorizontalGlue());
		mainPanel.add(sbPanel, gc);
		mainPanel.setAlignmentY(Component.TOP_ALIGNMENT);
		mainPanel.setBorder(BorderFactory.createEmptyBorder(3,3,3,3));
	}
	
	private ActionListener startGameListener = new ActionListener()
	{
		@Override
		public void actionPerformed(ActionEvent e) 
		{			
			startGameHandler();
		}
	};
	
	// listener for the buttons
	private ActionListener mainButtonListener = new ActionListener()
	{
		@Override
		public void actionPerformed(ActionEvent e) {
			if (!host) return;
			
			String[] args = e.getActionCommand().split(" ");
			int row = Integer.parseInt(args[1]);
			if (args[0].equals("Left"))
			{
				if (owners[row] == null)
				{
					// in a new game, always place the new investigator
					// in the first empty spot
					if (newGame)
					{
						while (row > 0 && owners[row - 1] == null)
						{
							row--;
						}
					}
					if (!playerListDisplay.isSelectionEmpty())
					{
						owners[row] = (String)playerListDisplay.getSelectedValue();
						investigators[row] = "< New Investigator >";
					}
				}
			}
			else if (args[0].equals("Right"))
			{
				if (owners[row] != null)
				{
					owners[row] = null;
					// in a new game, move everyone else up
					// otherwise, leave everyone alone
					if (newGame)
					{
						for (int i = row; i < 7; i++)
						{
							owners[i] = owners[i + 1];
							investigators[i] = investigators[i + 1];
						}
						owners[7] = null;
						investigators[7] = "< None >";
					}
				}
			}
			else if (args[0].equals("Up"))
			{
				if (row > 0 && owners[row] != null)
				{
					String temp = owners[row];
					owners[row] = owners[row - 1];
					owners[row - 1] = temp;
					temp = investigators[row];
					investigators[row] = investigators[row - 1];
					investigators[row - 1] = investigators[row];
				}
			}
			else if (args[0].equals("Down"))
			{
				if (row < 7 && owners[row] != null)
				{
					if (!newGame || owners[row + 1] != null)
					{
						String temp = owners[row];
						owners[row] = owners[row + 1];
						owners[row + 1] = temp;
						temp = investigators[row];
						investigators[row] = investigators[row + 1];
						investigators[row + 1] = investigators[row];
					}
				}
			}
			
			updateMainPanel();
			mainPanel.repaint();
			if (host)
			{
				server.serverInfoMessage("ownerList", requestForInfo("ownerList"));
			}
		}	
	};
	
	/**
	 * Initializes the chat box
	 */
	private void initChatBox()
	{
		chatBoxPanel = new JPanel();
		chatBoxPanel.setLayout(new BoxLayout(chatBoxPanel, BoxLayout.Y_AXIS));
		chatManager = new ChatManager(username);
		chatManager.setFixedSize(800, 160);
		
		JScrollPane scrollPane = new JScrollPane(chatManager.getChatBox());
		ImageUtils.setFixedSize(scrollPane, 800, 135);
		scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
		
		chatBoxPanel.add(scrollPane);
		chatBoxPanel.add(chatManager.getChatField());
		chatManager.getChatField().requestFocus();
		
		if (host)
		{
			chatManager.displayMessage(">>Listening server started on IP " + 
					server.getIPAddress() + " and port " + server.getPort());
		}
		else
		{
			chatManager.displayMessage(">>Connected to IP " + 
					client.getHostname() + " and port " + client.getPort());
		}
	}
	
	/**
	 * Syncs the display of the main panel with the underlying list of 
	 * players and investigators
	 */
	private void updateMainPanel()
	{
		for (int i = 0; i < 8; i++)
		{
			ownerLabels[i].setText(owners[i]);
			investigatorLabels[i].setText(investigators[i]);
			if (newGame && owners[i] == null)
			{
				investigatorLabels[i].setText("< None >");
			}
		}
		mainPanel.repaint();
	}
	
	/**
	 * Syncs the display of the player list with the underlying player list
	 */
	private void updatePlayerDisplay()
	{
		if (players.isEmpty()) return;
		String[] data = new String[players.size()];
		int index = 0;
		for (String player : players)
		{
			data[index] = player;
			index++;
		}
		playerListDisplay.setListData(data);
		playerListDisplay.repaint();
	}
	
	/**
	 * Checks if this instance is the host
	 * @return true if this is the host, false otherwise
	 */
	public boolean isHost()
	{
		return host;
	}
	
	private void startGameHandler()
	{
		if (host)
		{
			/* check that all clients are ready
			 * if they are, signal to them that the game is starting
			 * then start the game.
			 */
			synchronized(players)
			{
				if (!newGame)
				{
					//TODO: to check if all investigators are assigned owners
					return;
				}
				/* New game: count number of owners. Owners should have been
				 * condensed to the start of the array.
				 */
				StringBuffer ownerBuf = new StringBuffer();
				int invcount = 0;
				for (int i = 0; i < owners.length; i++)
				{
					if (i != 0) 
					{
						ownerBuf.append(",");
					}
					if (owners[i] != null)
					{
						ownerBuf.append(owners[i]);
						invcount++;
					}
					else
					{
						break;
					}
				}
				if (invcount == 0)
				{
					chatManager.displayMessage("# Must have at least one investigator to start game.");
					return;
				}
				boolean ready = readyCheck();
				if (!ready)
				{
					chatManager.displayMessage("# Cannot start game until all players are ready.");
					return;
				}
				if (!ownerCheck())
				{
					int val = JOptionPane.showConfirmDialog(this, "At least one player is " +
							"not assigned an investigator. Do you wish to proceed? " +
							"(that player will be an observer)", "Warning", 
							JOptionPane.YES_NO_OPTION);
					if (val != JOptionPane.YES_OPTION)
					{
						return;
					}
				}
				// load actual game
				GameData gdata = loader.loadGameData(invcount);
				if (gdata.isLoaded())
				{
					gdata.setPlayerNames(Arrays.copyOfRange(owners, 0, invcount));
					// get game options
					GameOptionsDialog gopt = new GameOptionsDialog(this, invcount, 
							gdata.getInvestigatorPool().size());
					gopt.setVisible(true);
					gopt.dispose();
					if (gopt.isCancelled())
					{
						return;
					}
					else
					{
						gdata.setDraftMode(gopt.isDraft());
						gdata.setInvestigatorSelectionValue(gopt.getValue());
					}
					//loadSampleStuff(gdata);
					GameScreen gs = new GameScreen(server, gdata, chatManager, players);
					dispose();
					//signal to clients that game is ready
					CommandMessage cmsg = new CommandMessage();

					cmsg.addCommand("startGame", String.valueOf(invcount));
					cmsg.addCommand("ownerList", ownerBuf.toString());
					cmsg.addCommand("draftMode", String.valueOf(gopt.isDraft()));
					cmsg.addCommand("selectionValue", String.valueOf(gopt.getValue()));
					server.serverCommandMessage(cmsg);
				}
				else
				{
					JOptionPane.showMessageDialog(null, "Error loading game data.", 
							"Error", JOptionPane.ERROR_MESSAGE);
				}
			}
		}
		else
		{
			if (!newGame)
			{
				//TODO: to check if all investigators are assigned owners
				return;
			}
			int invcount = 0;
			boolean hasOwner = false;
			synchronized(players)
			{
				for (int i = 0; i < owners.length; i++)
				{
					if (owners[i] == null) continue;
					invcount++;
					if (owners[i].equals(username)) hasOwner = true;
				}
			}
			if (invcount == 0)
			{
				chatManager.displayMessage("# Must have at least one investigator to start game.");
				return;
			}
			if (!hasOwner)
			{
				int val = JOptionPane.showConfirmDialog(this, "You have not been assigned an " +
						"investigator and will be playing as a guest. Proceed?", "Warning", 
						JOptionPane.YES_NO_OPTION);
				if (val != JOptionPane.YES_OPTION)
				{
					return;
				}
			}
			// signal that the client is ready
			if (loader.getNumMissingFiles() == 0)
			{
				CommandMessage cmsg = new CommandMessage("ready", "readyToStartGame");
				client.sendCommandMessage(cmsg);
			}
		}
	}
	
	/**
	 * Checks if all players are ready
	 * @return true if all players are ready, false otherwise
	 */
	private boolean readyCheck()
	{
		for (String name : players)
		{
			if (name.equals(hostusername)) continue;
			if (!readyPlayers.contains(name))
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Checks if all connected players are assigned as owners
	 * @return true if all connected players are owners, false otherwise
	 */
	private boolean ownerCheck()
	{
		for (int i = 0; i < players.size(); i++)
		{
			String p = players.get(i);
			boolean found = false;
			for (int j = 0; j < owners.length; j++)
			{
				if (owners[j] == null) continue;
				if (p.equals(owners[j]))
				{
					found = true;
				}
			}
			if (!found)
			{
				return false;
			}
		}
		return true;
	}
	
	/**
	 * Handler method for when players join
	 * @param playerName the player who joined
	 */
	private void playerJoined(String playerName)
	{
		chatManager.displayMessage(">> " + playerName + " has joined the game.");
		synchronized(players)
		{
			players.add(playerName);
		}
		updatePlayerDisplay();
	}
	
	/**
	 * Handler method for when players leave.
	 * @param playerName the player who left
	 */
	private void playerLeft(String playerName)
	{
		chatManager.displayMessage(">> " + playerName + " has left the game.");
		synchronized(players)
		{
			players.remove(playerName);
			readyPlayers.remove(playerName);
		}
		boolean removed = false;
		// update main panel
		for (int i = 0; i < 8; i++)
		{
			if (owners[i] != null && owners[i].equals(playerName))
			{
				owners[i] = null;
				removed = true;
			}
		}
		// move everyone up in a new game
		if (newGame && removed)
		{	
			int firstEmpty = -1;
			for (int i = 0; i < 7; i++)
			{
				if (owners[i] == null)
				{
					if (firstEmpty == -1)
					{
						firstEmpty = i;
					}
				}
				else
				{
					if (firstEmpty != -1)
					{
						owners[firstEmpty] = owners[i];
						investigators[firstEmpty] = investigators[i];
						owners[i] = null;
						investigators[i] = "< None >";
						firstEmpty++;
					}
				}
			}
		}
		
		updatePlayerDisplay();
		updateMainPanel();
	}
	
	/**
	 * The server is requested for a file
	 * @param filename the filename
	 * @return the file data
	 */
	private String requestForFile(String filename)
	{
		return loader.getFile(filename);
	}
	
	/**
	 * The server is requested for info
	 * @param query the query 
	 * @return the response to the query
	 */
	private String requestForInfo(String query)
	{
		if (query.equals("loadString"))
		{
			return loadString;
		}
		else if (query.equals("playerList"))
		{
			StringBuffer sb = new StringBuffer();
			for (String name : players)
			{
				if (sb.length() > 0) sb.append(",");
				sb.append(name);
			}
			return sb.toString();
		}
		else if (query.equals("hostUsername"))
		{
			return hostusername;
		}
		else if (query.equals("ownerList"))
		{
			StringBuffer sb = new StringBuffer();
			for (int i = 0; i < 8; i++)
			{
				if (i != 0) sb.append(",");
				if (owners[i] != null)
				{
					sb.append(owners[i]);
				}
			}
			return sb.toString();
		}
		else if (query.equals("masterData"))
		{
			return loader.getMasterData();
		}
		else if (query.equals("md5Info"))
		{
			return loader.getMD5Info();
		}
		return "";
	}
	
	/**
	 * The client receives a file from the server. The file is passed
	 * to the loader.
	 * @param filename the file name
	 * @param filedata the file data
	 */
	private void clientFileReceived(String filename, String filedata)
	{
		boolean res = loader.addFile(filename, filedata);
		int numDone = numFilesToLoad - loader.getNumMissingFiles();
		if (numDone == numFilesToLoad)
		{
			startGame.setText("Ready");
			startGame.setEnabled(true);
			chatManager.displayMessage(">> All files updated. Press Ready to begin. " +
			"Game will start when all players are ready.");
		}
		else
		{
			if (res)
			{
				chatManager.displayMessage(">> Files downloading, " + numDone + "/" + 
						numFilesToLoad + " complete.");
			}
			String file = loader.getMissingFiles().get(0);
			client.sendInfoMessage("fileRequest", file);
		}
	}
	
	/**
	 * The client receives info from the server
	 * @param query the query
	 * @param response the response to the query
	 */
	private void infoReceived(String query, String response)
	{
		if (query.equals("playerList"))
		{
			players.clear();
			String[] ps = response.split(",");
			for (String s : ps)
			{
				players.add(s);
			}
			updatePlayerDisplay();
		}
		else if (query.equals("playerJoined"))
		{
			playerJoined(response);
		}
		else if (query.equals("playerLeft"))
		{
			playerLeft(response);
		}
		else if (query.equals("hostUsername"))
		{
			hostusername = response;
			client.setHostUsername(response);
		}
		else if (query.equals("ownerList"))
		{
			String[] ps = response.split(",", 8);
			for (int i = 0; i < 8; i++)
			{
				if (ps[i].equals(""))
				{
					owners[i] = null;
					investigators[i] = null;
				}
				else
				{
					owners[i] = ps[i];
					if (newGame)
					{
						investigators[i] = "< New Investigator >";
					}
				}
			}
			updateMainPanel();
		}
		else if (query.equals("md5Info"))
		{
			boolean res = loader.compareMD5(response);
			if (!res)
			{
				shutdown();
			}
			numFilesToLoad = loader.getNumMissingFiles();
			if (numFilesToLoad == 0)
			{
				startGame.setText("Ready");
				startGame.setEnabled(true);
				chatManager.displayMessage(">> All files updated. Press Ready to begin. " +
						"Game will start when all players are ready.");
			}
			else
			{
				chatManager.displayMessage(">> Downloading required files, 0/" + 
						numFilesToLoad + " complete.");
				String file = loader.getMissingFiles().get(0);
				client.sendInfoMessage("fileRequest", file);
			}
		}
	}
	
	/**
	 * Shuts down this lobby screen, especially in the case of unrecoverable
	 * errors.
	 */
	private void shutdown()
	{
		if (client != null)
		{
			client.stopClient();
		}
		if (server != null)
		{
			server.stopServer();
		}
		dispose();
	}
	
	public void messageReceived(Message m, String sender)
	{
		if (m instanceof InfoMessage)
		{
			InfoMessage imsg = (InfoMessage) m;
			AHLogger.info("LobbyScreen", "Info from " + sender + ": " + imsg.getRequest() + 
					"," + imsg.getResponse() + ";");
			if (host)
			{
				// When info messages are received by the server, give it
				// to the client
				if (imsg.getRequest().equals("fileRequest"))
				{
					String filetext = requestForFile(imsg.getResponse());
					if (filetext != null)
					{
						server.serverFileMessage(imsg.getResponse(), filetext, sender);
					}
				}
				else
				{
					String info = requestForInfo(imsg.getRequest());
					server.serverInfoMessage(imsg.getRequest(), info, sender);
				}
			}
			else
			{
				infoReceived(imsg.getRequest(), imsg.getResponse());
			}
		}
		else if (m instanceof FileMessage)
		{
			FileMessage fmsg = (FileMessage)m;
			AHLogger.info("LobbyScreen", "File from " + sender + ": " + fmsg.getFilename() + 
					"(size " + fmsg.getFiledata().length() + ")");
			if (!host)
			{
				clientFileReceived(fmsg.getFilename(), fmsg.getFiledata());
			}
		}
		else if (m instanceof CommandMessage)
		{
			// receives a command message. Only the "ready" and "startGame" commands will
			// be used in the lobby
			CommandMessage cmsg = (CommandMessage)m;
			AHLogger.info("LobbyScreen", "Command from " + sender + ": (" + cmsg.getActivePlayer() +
					") " + cmsg.toString());
			if (host)
			{
				Command command = cmsg.getFirstCommand();
				if (command != null)
				{
					if (command.cmd.equals("ready"))
					{
						readyPlayers.add(sender);
						if (readyCheck())
						{
							chatManager.displayMessage("# " + sender + " is ready. All players are now ready.");
						}
						else
						{
							chatManager.displayMessage("# " + sender + " is ready.");
						}
					}
				}
			}
			else
			{
				Command command = cmsg.removeFirstCommand();
				if (command != null)
				{
					if (command.cmd.equals("startGame"))
					{
						if (loader.getNumMissingFiles() != 0)
						{
							client.sendErrorMessage("Unable to start game, files not loaded.");
							return;
						}
						int invcount = Integer.parseInt(command.details);
						command = cmsg.removeFirstCommand();
						String[] ownerArr = command.details.split(",");
						if (!Arrays.equals(Arrays.copyOfRange(owners, 0, invcount), ownerArr))
						{
							AHLogger.warning("LobbyScreen", "Owner lists do not match, using version from server.");
						}
						
						GameData gdata = loader.loadGameData(invcount);
						if (gdata == null)
						{
							client.sendErrorMessage("Unable to start game, files not loaded.");
							return;
						}
						if (gdata.isLoaded())
						{
							gdata.setPlayerNames(ownerArr);
							command = cmsg.removeFirstCommand();
							if (!command.cmd.equals("draftMode"))
							{
								AHLogger.warning("LobbyScreen", "malformed draft mode packet");
								client.sendErrorMessage("Malformed game start packet, " +
										"draft mode not specified");
							}
							else
							{
								gdata.setDraftMode(Boolean.parseBoolean(command.details));
							}
							command = cmsg.removeFirstCommand();
							if (!command.cmd.equals("selectionValue"))
							{
								AHLogger.warning("LobbyScreen", "malformed draft mode packet");
								client.sendErrorMessage("Malformed game start packet, " +
										"selection value not specified");
							}
							else
							{
								gdata.setInvestigatorSelectionValue(Integer.parseInt(command.details));
							}
							
							//loadSampleStuff(gdata);
							GameScreen gs = new GameScreen(client, gdata, chatManager);
							dispose();
						}
						else
						{
							JOptionPane.showMessageDialog(null, "Error loading game data.", 
									"Error", JOptionPane.ERROR_MESSAGE); 
						}
					}
				}
			}
		}
	}
	
	@Override
	public void displayInfo(String s)
	{
		chatManager.displayMessage(s);
	}
	
	public void textReceived(String s, String sender)
	{
		displayInfo(s);
	}
	
	public void newConnection(String username)
	{
		playerJoined(username);
	}
	
	public void connectionLost(String username)
	{
		playerLeft(username);
	}
	
	/* --------------------- Utility Method -------------------- */
	
	private void inputGameOptions(int numPlayers, int poolSize)
	{
		GameOptionsDialog gopt = new GameOptionsDialog(this, numPlayers, poolSize);
		gopt.setVisible(true);
		gopt.dispose();
		if (!gopt.isCancelled())
		{
			boolean draft = gopt.isDraft();
			int val = gopt.getValue();
		}
	}
	
	/* --------------- WINDOW LISTENER INTERFACE --------------- */
	
	@Override
	public void windowActivated(WindowEvent arg0) {
		
	}

	@Override
	public void windowClosed(WindowEvent arg0) {
		
	}

	@Override
	public void windowClosing(WindowEvent e) {
		int response = JOptionPane.showConfirmDialog(this, "Really quit?", 
				"Quit", JOptionPane.OK_CANCEL_OPTION);
		if (response == JOptionPane.OK_OPTION)
		{
			shutdown();
		}
	}

	@Override
	public void windowDeactivated(WindowEvent arg0) {

	}

	@Override
	public void windowDeiconified(WindowEvent arg0) {

	}

	@Override
	public void windowIconified(WindowEvent arg0) {
		
	}

	@Override
	public void windowOpened(WindowEvent arg0) {

	}
	
	/* --------------- END WINDOW LISTENER INTERFACE ------------ */
	
	// for testing purposes only
	private static void sampleLoad(GameData gdata)
	{
		ArrayList<File> files1 = new ArrayList<File>();
		files1.add(new File("xml/base/basecards.xml"));
		gdata.loadInvCardsFromFiles(files1);
		ArrayList<File> files2 = new ArrayList<File>();
		files2.add(new File("xml/base/arkhamchars.xml"));
		gdata.loadInvestigatorsFromFiles(files2);
		ArrayList<File> files3 = new ArrayList<File>();
		files3.add(new File("xml/base/arkhammonsters.xml"));
		gdata.loadMonstersFromFiles(files3);
		ArrayList<File> files4 = new ArrayList<File>();
		files4.add(new File("xml/base/arkhamaos.xml"));
		gdata.loadAncientOnesFromFiles(files4);
		ArrayList<File> filenames5 = new ArrayList<File>();
		gdata.loadBoardsFromFiles(new File("xml/base/arkhamboard.xml"), filenames5);
		filenames5.add(new File("xml/base/resources.xml"));
		gdata.loadResourcesFromFiles(filenames5);
		ArrayList<File> filenames6 = new ArrayList<File>();
		filenames6.add(new File("xml/base/arkhammain.xml"));
		gdata.loadEncountersFromFiles(filenames6);
	}
	
	private static void loadSampleStuff(GameData gdata)
	{
		AncientOne ao = GameData.retrieveByName(gdata.getAOPool(), "Yog-Sothoth");
		gdata.setAncientOne(ao);
		Board b = gdata.getMainBoard();
		Location loc = b.getLocation("Woods");
		loc.setClues(2);
		loc = b.getLocation("Independence Square");
		gdata.openNewGateOn(loc);
		loc = b.getLocation("Hibb's Roadhouse");
		loc.setClosed(true);
		loc = b.getLocation("Unvisited Isle");
		loc.setSealed(true);
		Investigator inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Sister Mary");
		inv.getInvcards().add(gdata.getCommonDeck().randomDeal());
		inv.getInvcards().add(gdata.getUniqueDeck().randomDeal());
		inv.getInvcards().add(gdata.getSpellDeck().randomDeal());
		inv.getInvcards().add(gdata.getSkillDeck().randomDeal());
		inv.getInvcards().add(gdata.getAllyDeck().randomDeal());
		inv.getStatusEffects().add(gdata.getPersonalEffect("Blessing"));
		inv.getStatusEffects().add(gdata.getPersonalEffect("Curse"));
		inv.getStatusEffects().add(gdata.getPersonalEffect("Retainer"));
		inv.getStatusEffects().add(gdata.getPersonalEffect("Deputy of Arkham"));
		inv.getStatusEffects().add(gdata.getPersonalEffect("Silver Twilight Lodge Membership"));
		
		loc = b.getLocation("South Church");
		gdata.moveInvestigatorTo(inv, loc);
		loc = b.getLocation("Uptown");
		Monster mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, loc);
		mon = GameData.removeByName(gdata.getMonsters(), "Ghost");
		gdata.moveMonsterTo(mon, loc);
		loc = b.getLocation("Graveyard");
		mon = GameData.removeByName(gdata.getMonsters(), "Mi-go");
		gdata.moveMonsterTo(mon, loc);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, loc);
		gdata.openNewGateOn(loc);
		loc = b.getLocation("Black Cave");
		loc.setSealed(true);
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Jenny Barnes");
		gdata.moveInvestigatorTo(inv, loc);
		
		OtherWorld ow = gdata.getMainBoard().getOtherWorld("Plateau of Leng");
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Joe Diamond");
		gdata.moveInvestigatorTo(inv, ow.getSegment(1));
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Vincent Lee");
		gdata.moveInvestigatorTo(inv, ow.getSegment(1));
		
		loc = b.getLocation("Administration");
		gdata.openNewGateOn(loc);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, loc);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, loc);
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Mandy Thompson");
		gdata.moveInvestigatorTo(inv, loc);
		LocationContainer con = b.getLocationContainer(Board.SKY_NAME);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, con);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, con);
		con = b.getLocationContainer("Science Building");
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Gloria Goldberg");
		gdata.moveInvestigatorTo(inv, con);
		inv = GameData.retrieveByName(gdata.getInvestigatorPool(), "Monterey Jack");
		gdata.moveInvestigatorTo(inv, con);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, con);
		mon = gdata.getMonsters().remove(0);
		gdata.moveMonsterTo(mon, con);
		
	}
}
